﻿//---------------------------------------------------------------------------
//
//   ExportRIB
//
//          Copyright(C) 1999, O.Mizno
//
//    　RIB(RenderMan)形式を出力するエクスポートプラグインのサンプル。
//    　作成したDLLは"Plugins\Export"フォルダに入れる必要がある。
//    　RenderManには詳しくないので、このサンプルではあまり多くの情報を出
//　　力していない。
//
//---------------------------------------------------------------------------

#define WIN32_LEAN_AND_MEAN
#include "tri_stripper.h"
#include "verifyObject.hpp"
#include "verifyMaterial.hpp"
#include "M3GWriter.hpp"
#include "writeCamera.hpp"
#include "writeAppearance.hpp"
#include "writeCompositingMode.hpp"
#include "writeColors.hpp"
#include "writeHeader.hpp"
#include "writeImage.hpp"
#include "writeLightAmbient.hpp"
#include "writeLightDirectional.hpp"
#include "writeMesh.hpp"
#include "writeMaterial.hpp"
#include "writeNormals.hpp"
#include "writePositions.hpp"
#include "writeTexCoords.hpp"
#include "writeTriangleStripArray.hpp"
#include "writeTexture.hpp"
#include "writeVertexBuffer.hpp"
#include "writeWorld.hpp"
#include "Log.hpp"
#include <algorithm>
#include <windows.h>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include "MQPlugin.hpp"
using namespace triangle_stripper;
using namespace std;
using namespace m3g;


BOOL SaveM3G(const char *filename, MQDocument doc);


//---------------------------------------------------------------------------
//  DllMain
//---------------------------------------------------------------------------
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	//プラグインとしては特に必要な処理はないので、何もせずにTRUEを返す
    return TRUE;
}

//---------------------------------------------------------------------------
//  MQGetPlugInID
//    プラグインIDを返す。
//    この関数は起動時に呼び出される。
//---------------------------------------------------------------------------
MQPLUGIN_EXPORT void MQGetPlugInID(DWORD *Product, DWORD *ID)
{
	// プロダクト名(制作者名)とIDを、全部で64bitの値として返す
	// 値は他と重複しないようなランダムなもので良い
	*Product = 0x71A2702A;
	*ID      = 0x85FD11E0;
}

//---------------------------------------------------------------------------
//  MQGetPlugInName
//    プラグイン名を返す。
//    この関数は[プラグインについて]表示時に呼び出される。
//---------------------------------------------------------------------------
MQPLUGIN_EXPORT const char *MQGetPlugInName(void)
{
	// プラグイン名
	return "M3G Exporter   Copyright(C) 2011, tueda";
}

//---------------------------------------------------------------------------
//  MQGetPlugInType
//    プラグインのタイプを返す。
//    この関数は起動時に呼び出される。
//---------------------------------------------------------------------------
MQPLUGIN_EXPORT int MQGetPlugInType(void)
{
	// 出力用プラグインである
	return MQPLUGIN_TYPE_EXPORT;
}

//---------------------------------------------------------------------------
//  MQEnumFileType
//    入力または出力可能なファイルタイプを返す。
//    ファイルタイプは別名保存時のダイアログに表示される。
//    この関数は起動時に呼び出される。
//---------------------------------------------------------------------------
MQPLUGIN_EXPORT const char *MQEnumFileType(int index)
{
	switch(index){
	case 0: return "M3G (*.m3g)";
	}
	return NULL;
}

//---------------------------------------------------------------------------
//  MQEnumFileExt
//    入力または出力可能な拡張子を返す。
//    この関数は起動時に呼び出される。
//---------------------------------------------------------------------------
MQPLUGIN_EXPORT const char *MQEnumFileExt(int index)
{
	switch(index){
	case 0: return "m3g";
	}
	return NULL;
}

//---------------------------------------------------------------------------
//  MQExportFile
//    [別名で保存]で保存するときに呼び出される。
//---------------------------------------------------------------------------
MQPLUGIN_EXPORT BOOL MQExportFile(int index, const char *filename, MQDocument doc)
{
	switch(index){
	case 0: return SaveM3G(filename, doc);
	}
	return FALSE;
}


//---------------------------------------------------------------------------
// M3G形式で出力
//---------------------------------------------------------------------------
BOOL SaveM3G (const char *filename, MQDocument doc)
{
	Log:: open ("m3g-exporter-for-metasequoia.log");

    MQScene scene = doc->GetScene(0);

    // 全てのオブジェクトを1つに合成した作業用オブジェクトの作成
    MQObject merged_obj = MQ_CreateObject();

    // 法線だけは自分で管理する
    vector<MQPoint> merged_normals;

    for (int i = 0; i < (int)doc->GetObjectCount(); i++)
    {
        vector<MQPoint> normals;

        MQObject obj = doc->GetObject (i);
        if (obj == NULL || obj->GetVisible() == 0) 
			continue;

		// フリーズなどにより内容が変更されてしまうので、オブジェクトを複製しておく
		obj = obj->Clone();

        // 曲面などの属性をフリーズ
        obj->Freeze (MQOBJECT_FREEZE_ALL);

        // M3Gでエクスポートしやすいようにオブジェクトを修正
        verifyObject (doc, obj, &normals);


        // 作業用オブジェクトに合成する
   		merged_obj->Merge(obj);
        merged_normals.insert (merged_normals.end(), normals.begin(), normals.end());

		// 複製したオブジェクトはもう不要なので破棄
		obj->DeleteThis();
    }

    int vertex_count = merged_obj->GetVertexCount();
    if (vertex_count > 65535) {
        Log:: print ("頂点数(%d)がM3Gの仕様上の上限である65535を超えました。このモデルはエクスポートできません。", vertex_count);
        merged_obj->DeleteThis();
    	Log:: close ();
        return false;
    }

    // M3Gでエクスポートしやすいようにマテリアルを修正
    // （マテリアルを追加するので後で解放すること）
    int default_material;
    verifyMaterial (doc, merged_obj, &default_material);


	// M3Gライター
	Log:: print ("Create writer.\n");
	M3GWriter* writer = new M3GWriter (filename);
	
	// シグネチャ
	Log:: print ("Write M3G signature.\n");
	writer->writeID ();


	// セクション１
	{
		Log:: print ("Start session 1.\n");
		Log:: print ("Write section.\n");
		writer->startSection (0);   // 0=uncompress

		// ヘッダー
		Log:: print ("Write Header.\n");
		writeHeader (writer);
	
		Log:: print ("End session 1.\n");
		writer->endSection ();
	}

	// セクション２
	{
		unsigned int         positions_index;
		unsigned int         normals_index;
		unsigned int         colors_index;
		unsigned int         tex_coords_index;
		unsigned int         vertex_buffer_index;
		vector<unsigned int> index_indices;
		vector<unsigned int> image_indices;
		vector<unsigned int> texture_indices;
		vector<unsigned int> material_indices;
		vector<unsigned int> cmode_indices;
		vector<unsigned int> appearance_indices;
		unsigned int         mesh_index;
		unsigned int         ambient_light_index;
		unsigned int         directional_light_index;
		unsigned int         camera_index;
		unsigned int         world_index;
		vector<unsigned int> child_indices;

		Log:: print ("Start session 2.\n");
		Log:: print ("Write session.\n");
		writer->startSection (1);   // 1=compressed
	
		// 位置
		float positions_scale   = 1;
        float positions_bias[3] = {0,0,0};
		positions_index = writePositions (writer, merged_obj, &positions_scale, positions_bias);


		// 法線
		float normals_scale   = 1;
        float normals_bias[3] = {0,0,0};
		normals_index = writeNormals (writer, merged_obj, merged_normals, &normals_scale, normals_bias);
       
		// 頂点カラー
		float colors_scale   = 1;
        float colors_bias[4] = {0,0,0,0};
		colors_index = writeColors (writer, doc, merged_obj, &colors_scale, colors_bias);
        
		// テクスチャーUV
		float tex_coords_scale   = 1;
        float tex_coords_bias[3] = {0,0,0};
		tex_coords_index = writeTexCoords (writer, doc, merged_obj, &tex_coords_scale, tex_coords_bias);
        
		// 頂点バッファー
		vertex_buffer_index = writeVertexBuffer (writer, merged_obj,
												positions_index , positions_scale , positions_bias ,
												normals_index   , normals_scale   , normals_bias   ,
												colors_index    , colors_scale    , colors_bias    ,
												tex_coords_index, tex_coords_scale, tex_coords_bias);

		// 頂点インデックス
		index_indices = writeTriangleStripArray (writer, doc, merged_obj);

		// テクスチャー画像
		image_indices = writeImage (writer, doc);

		// テクスチャー
		texture_indices = writeTexture (writer, doc, image_indices);
        
		// マテリアル
		material_indices = writeMaterial (writer, doc);
        
		// コンポジティング．モード
		cmode_indices = writeCompositingMode (writer, doc);
       
        // アピアランス
		appearance_indices = writeAppearance (writer, doc, merged_obj, material_indices, cmode_indices, texture_indices);

		// メッシュ
		mesh_index = writeMesh (writer, merged_obj, vertex_buffer_index, index_indices, appearance_indices);

		// ライト
		ambient_light_index     = writeLightAmbient (writer, scene);
		directional_light_index = writeLightDirectional (writer, scene);

		// カメラ
		camera_index = writeCamera (writer, scene);
		
		// ワールド
		child_indices.push_back (mesh_index             );
		child_indices.push_back (directional_light_index);
		child_indices.push_back (ambient_light_index    );
		child_indices.push_back (camera_index           );
		world_index = writeWorld (writer, camera_index, 0, child_indices);

		Log:: print ("End section.\n");
		writer->endSection ();
	}

	Log:: print ("Delete writer.\n");
	delete writer;

    doc->DeleteMaterial (default_material);
    merged_obj->DeleteThis();

	Log:: close ();

	return TRUE;
}

